#include "EpollPoll.h"

#include <stddef.h>

#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

CEpollPoll::CEpollPoll()
    :m_iEpFd(0), m_iSocketCount(0), m_iTotalCount(0), m_pEvent(NULL)
{
}


CEpollPoll::~CEpollPoll()
{
    if (m_iEpFd != 0)
    {
        close(m_iEpFd);
    }
    if (m_pEvent != NULL)
    {
        delete[]m_pEvent;
        m_pEvent = NULL;
    }
}

bool CEpollPoll::Init(const int iReadySize)
{
    if (!CPoll::Init(iReadySize))
    {
        return false;
    }

    if (m_iEpFd != 0)
    {
        close(m_iEpFd);
    }

    m_iEpFd = epoll_create(iReadySize);
    m_iTotalCount = iReadySize;
    m_iSocketCount = 0;
    
    if (m_pEvent != NULL)
    {
        delete[]m_pEvent;
    }

    m_pEvent = new struct epoll_event[iReadySize];

    if (m_pEvent == NULL)
    {
        return false;
    }

    return true;
}

bool CEpollPoll::AddReadSocket(const int iSocket)
{
    if (m_iSocketCount >= m_iTotalCount)
    {
        return false;
    }

    struct epoll_event stEpollEvent;
    stEpollEvent.events = EPOLLIN;
    stEpollEvent.data.u64 = 0;
    stEpollEvent.data.fd = iSocket;

    if (epoll_ctl(m_iEpFd, EPOLL_CTL_ADD, iSocket, &stEpollEvent) == -1)
    {
        return false;
    }

    ++m_iSocketCount;
    return true;
}

void CEpollPoll::RemoveReadSocket(const int iSocket)
{
    struct epoll_event stEpollEvent;
    stEpollEvent.events = EPOLLIN;
    stEpollEvent.data.u64 = 0;
    stEpollEvent.data.fd = iSocket;

    if (epoll_ctl(m_iEpFd, EPOLL_CTL_DEL, iSocket, &stEpollEvent) == 0)
    {
        --m_iSocketCount;
    }
}

bool CEpollPoll::AddWriteSocket(const int iSocket)
{
    if (m_iSocketCount >= m_iTotalCount)
    {
        return false;
    }

    struct epoll_event stEpollEvent;
    stEpollEvent.events = EPOLLOUT;
    stEpollEvent.data.u64 = 0;
    stEpollEvent.data.fd = iSocket;

    if (epoll_ctl(m_iEpFd, EPOLL_CTL_ADD, iSocket, &stEpollEvent) == -1)
    {
        return false;
    }
    ++m_iSocketCount;
    return true;
}

void CEpollPoll::RemoveWriteSocket(const int iSocket)
{
    struct epoll_event stEpollEvent;
    stEpollEvent.events = EPOLLOUT;
    stEpollEvent.data.u64 = 0;
    stEpollEvent.data.fd = iSocket;

    if (epoll_ctl(m_iEpFd, EPOLL_CTL_DEL, iSocket, &stEpollEvent) == 0)
    {
        --m_iSocketCount;
    }
}

int CEpollPoll::Polling(int iWaitTime)
{
    if (m_pEvent == NULL || m_iEpFd <=0)
    {
        return 0;
    }

    int iCount = epoll_wait(m_iEpFd, m_pEvent, m_iSocketCount, iWaitTime);

    if (iCount > 0)
    {
        for (int i = 0; i < iCount; ++i)
        {
            struct epoll_event *pEvent = m_pEvent + i;

            if (pEvent->events & EPOLLIN)
            {
                AddReadSocket(pEvent->data.fd);
            }
            if ((pEvent->events & EPOLLOUT) || (pEvent->events & EPOLLERR) || (pEvent->events & EPOLLHUP))
            {
                AddWriteSocket(pEvent->data.fd);
            }
        }
    }

    return 0;
}

const char * CEpollPoll::GetName()
{
    return "epoll";
}

